"""
恐贪指标计算服务层
负责恐贪指标的核心计算逻辑
提供可重用的计算服务，支持单个和批量计算
"""

import pandas as pd
import numpy as np
from datetime import datetime
from typing import Dict, List, Optional
import logging

logger = logging.getLogger(__name__)

# 全局常量配置
TANH_SCALING_FACTOR = 0.006  # tanh函数的缩放因子，控制曲线的陡峭程度
TANH_MULTIPLIER = 170  # tanh函数的乘数因子，控制输出范围
ROUND_PRECISION = 3  # 所有数值的保留小数位数


class FearGreedProcessor:
    """
    恐贪指标计算服务类
    提供纯计算功能，不涉及数据获取
    """
    
    def __init__(self):
        """初始化计算服务"""
        # 指标权重 - 平衡各指标权重，减少极端波动
        self.indicators = {
            'momentum': 0.30,       # 动量指标权重 (主要指标)
            'volatility': 0.25,     # 波动率指标权重 (次要指标)
            'market_volume': 0.25,  # 市场成交量权重 (次要指标)
            'put_call_ratio': 0.20 # 看跌看涨比率权重 (辅助指标)
        }
    
    def calculate_fear_greed_index_from_data(self, stock_data: pd.DataFrame, symbol: str = None) -> Dict:
        """
        从股票数据计算恐贪指标
        
        Args:
            stock_data: 包含OHLCV数据的DataFrame，必须包含['Open', 'High', 'Low', 'Close', 'Volume']列
            symbol: 股票代码（可选，用于日志）
            
        Returns:
            包含恐贪指标计算结果的字典
        """
        try:
            if stock_data.empty or len(stock_data) < 20:
                raise ValueError(f"数据不足，至少需要20天数据进行计算")
            
            # 验证必要的列
            required_columns = ['Open', 'High', 'Low', 'Close', 'Volume']
            missing_columns = [col for col in required_columns if col not in stock_data.columns]
            if missing_columns:
                raise ValueError(f"缺少必要的数据列: {missing_columns}")
            
            # 计算各个子指标
            momentum_score = self._calculate_momentum_score(stock_data, symbol)
            volatility_score = self._calculate_volatility_score(stock_data)
            volume_score = self._calculate_volume_score(stock_data)
            put_call_score = self._calculate_put_call_score(stock_data)
            
            # 计算综合恐贪指标
            fear_greed_index = (
                momentum_score * self.indicators['momentum'] +
                volatility_score * self.indicators['volatility'] +
                volume_score * self.indicators['market_volume'] +
                put_call_score * self.indicators['put_call_ratio']
            )
            
            # 使用tanh函数进行平滑映射，避免硬截断
            fear_greed_index = TANH_MULTIPLIER * np.tanh(fear_greed_index * TANH_SCALING_FACTOR)
            
            # 确定市场情绪状态和投资建议
            sentiment = self._get_sentiment_level(fear_greed_index)
            investment_advice = self._get_investment_advice(fear_greed_index)
            
            return {
                'fear_greed_index': round(fear_greed_index, ROUND_PRECISION),
                'sentiment': sentiment,
                'investment_advice': investment_advice,
                'components': {
                    'momentum': round(momentum_score, ROUND_PRECISION),
                    'volatility': round(volatility_score, ROUND_PRECISION),
                    'volume': round(volume_score, ROUND_PRECISION),
                    'put_call_ratio': round(put_call_score, ROUND_PRECISION)
                },
                'timestamp': datetime.now().isoformat()
            }
            
        except Exception as e:
            logger.error(f"计算恐贪指标失败: {str(e)}")
            raise
    
    def _calculate_momentum_score(self, data: pd.DataFrame, symbol: str = None) -> float:
        """计算优化后的动量指标分数"""
        try:
            # 创建数据副本避免SettingWithCopyWarning
            data = data.copy()
            
            # 计算对数收益率和指数加权动量
            data.loc[:, 'log_returns'] = np.log(data['Close'] / data['Close'].shift(1))
            
            # 使用指数加权移动平均计算动量
            ema_decay = 0.15
            data.loc[:, 'momentum_ewm_5d'] = data['log_returns'].ewm(span=5, adjust=False).mean()
            data.loc[:, 'momentum_ewm_20d'] = data['log_returns'].ewm(span=20, adjust=False).mean()
            data.loc[:, 'momentum_long_term'] = data['log_returns'].ewm(alpha=ema_decay, adjust=False).mean()
            
            # 计算价格相对于移动平均的位置
            data.loc[:, 'price_ma_5d'] = data['Close'].ewm(span=5, adjust=False).mean()
            data.loc[:, 'price_ma_20d'] = data['Close'].ewm(span=20, adjust=False).mean()
            data.loc[:, 'price_ma_long_term'] = data['Close'].ewm(alpha=ema_decay, adjust=False).mean()
            data.loc[:, 'price_position_5d'] = (data['Close'] - data['price_ma_5d']) / data['price_ma_5d']
            data.loc[:, 'price_position_20d'] = (data['Close'] - data['price_ma_20d']) / data['price_ma_20d']
            data.loc[:, 'price_position_long_term'] = (data['Close'] - data['price_ma_long_term']) / data['price_ma_long_term']
            
            # 计算改进的RSI
            delta = data['Close'].diff()
            gain = delta.where(delta > 0, 0).ewm(span=14, adjust=False).mean()
            loss = (-delta.where(delta < 0, 0)).ewm(span=14, adjust=False).mean()
            rs = gain / loss
            data.loc[:, 'RSI_ewm'] = 100 - (100 / (1 + rs))
            
            # 获取最新值并处理NaN
            latest_rsi = data['RSI_ewm'].iloc[-1]
            latest_momentum_5d = data['momentum_ewm_5d'].iloc[-1]
            latest_momentum_20d = data['momentum_ewm_20d'].iloc[-1]
            latest_momentum_long_term = data['momentum_long_term'].iloc[-1]
            latest_price_pos_5d = data['price_position_5d'].iloc[-1]
            latest_price_pos_20d = data['price_position_20d'].iloc[-1]
            latest_price_pos_long_term = data['price_position_long_term'].iloc[-1]
            
            # 处理NaN值
            if pd.isna(latest_rsi):
                latest_rsi = 50.0
            if pd.isna(latest_momentum_5d):
                latest_momentum_5d = 0.0
            if pd.isna(latest_momentum_20d):
                latest_momentum_20d = 0.0
            if pd.isna(latest_price_pos_5d):
                latest_price_pos_5d = 0.0
            if pd.isna(latest_price_pos_20d):
                latest_price_pos_20d = 0.0
            if pd.isna(latest_momentum_long_term):
                latest_momentum_long_term = 0.0
            if pd.isna(latest_price_pos_long_term):
                latest_price_pos_long_term = 0.0
            
            # 非线性转换与分数生成
            rsi_sentiment = np.tanh((latest_rsi - 50) / 35)
            momentum_5d_sentiment = np.tanh(latest_momentum_5d * 100)
            momentum_20d_sentiment = np.tanh(latest_momentum_20d * 100)
            momentum_long_term_sentiment = np.tanh(latest_momentum_long_term * 100)
            price_pos_5d_sentiment = np.tanh(latest_price_pos_5d * 10)
            price_pos_20d_sentiment = np.tanh(latest_price_pos_20d * 10)
            price_pos_long_term_sentiment = np.tanh(latest_price_pos_long_term * 20)
            
            # 综合动量分数
            final_momentum_score = (
                rsi_sentiment * 0.25 +
                momentum_5d_sentiment * 0.20 +
                momentum_20d_sentiment * 0.15 +
                momentum_long_term_sentiment * 0.10 +
                price_pos_5d_sentiment * 0.15 +
                price_pos_20d_sentiment * 0.10 +
                price_pos_long_term_sentiment * 0.05
            ) * 100
            
            final_momentum_score = TANH_MULTIPLIER * np.tanh(final_momentum_score * TANH_SCALING_FACTOR)
            
            logger.debug(f"动量计算 - RSI: {latest_rsi:.2f}, 最终分数: {final_momentum_score:.2f}")
            
            return final_momentum_score
                    
        except Exception as e:
            logger.error(f"计算动量指标失败: {str(e)}")
            return 0.0
    
    def _calculate_volatility_score(self, data: pd.DataFrame) -> float:
        """计算优化后的波动率指标分数"""
        try:
            # 创建数据副本
            data = data.copy()
            
            # 计算对数收益率
            data.loc[:, 'log_returns'] = np.log(data['Close'] / data['Close'].shift(1))
            
            # 使用指数加权移动标准差计算波动率
            time_window = 24
            data.loc[:, 'volatility_ewm'] = data['log_returns'].ewm(span=time_window, adjust=False).std() * np.sqrt(252)
            
            # 计算布林带
            data.loc[:, 'BB_middle'] = data['Close'].rolling(window=time_window).mean()
            data.loc[:, 'BB_std'] = data['Close'].rolling(window=time_window).std()
            data.loc[:, 'BB_upper'] = data['BB_middle'] + (data['BB_std'] * 2)
            data.loc[:, 'BB_lower'] = data['BB_middle'] - (data['BB_std'] * 2)
            data.loc[:, 'BB_position'] = (data['Close'] - data['BB_lower']) / (data['BB_upper'] - data['BB_lower'])
            
            # 波动率Z-Score归一化
            historical_vol_mean = data['volatility_ewm'].rolling(window=60).mean()
            historical_vol_std = data['volatility_ewm'].rolling(window=60).std()
            data.loc[:, 'volatility_zscore'] = (data['volatility_ewm'] - historical_vol_mean) / historical_vol_std
            
            # 获取最新值
            latest_volatility_zscore = data['volatility_zscore'].iloc[-1]
            latest_bb_position = data['BB_position'].iloc[-1]
            
            # 处理NaN值
            if pd.isna(latest_volatility_zscore):
                latest_volatility_zscore = 0.0
            if pd.isna(latest_bb_position):
                latest_bb_position = 0.5
            
            # 非线性转换
            volatility_sentiment = np.tanh(-latest_volatility_zscore)
            bb_sentiment = np.tanh((latest_bb_position - 0.5) * 4)
            
            # 最终综合指标
            final_volatility_score = (volatility_sentiment * 0.6 + bb_sentiment * 0.4) * 100
            final_volatility_score = TANH_MULTIPLIER * np.tanh(final_volatility_score * TANH_SCALING_FACTOR)
            
            logger.debug(f"波动率计算 - Z-Score: {latest_volatility_zscore:.4f}, 最终分数: {final_volatility_score:.2f}")
            
            return final_volatility_score
            
        except Exception as e:
            logger.error(f"计算波动率指标失败: {str(e)}")
            return 0.0
    
    def _calculate_volume_score(self, data: pd.DataFrame) -> float:
        """计算优化后的成交量指标分数"""
        try:
            # 创建数据副本
            data = data.copy()
            
            # 计算成交量相关指标
            data.loc[:, 'volume_ewm_20d'] = data['Volume'].ewm(span=20, adjust=False).mean()
            data.loc[:, 'volume_ratio_ewm'] = data['Volume'] / data['volume_ewm_20d']
            data.loc[:, 'volume_rs'] = data['Volume'] / data['Volume'].rolling(window=60).mean()
            
            # 计算价格成交量趋势
            data.loc[:, 'price_volume_trend'] = (
                (data['Close'] - data['Close'].shift(1)) * data['Volume']
            ).cumsum()
            
            # 计算成交量加权价格变化
            data.loc[:, 'vwap_change'] = (
                (data['Close'] * data['Volume']).rolling(window=20).sum() / 
                data['Volume'].rolling(window=20).sum()
            ).pct_change(fill_method=None)
            
            # 获取最新值
            latest_volume_ratio = data['volume_ratio_ewm'].iloc[-1]
            latest_volume_rs = data['volume_rs'].iloc[-1]
            latest_pvt = data['price_volume_trend'].iloc[-1]
            latest_vwap_change = data['vwap_change'].iloc[-1]
            
            # 处理NaN值
            if pd.isna(latest_volume_ratio):
                latest_volume_ratio = 1.0
            if pd.isna(latest_volume_rs):
                latest_volume_rs = 1.0
            if pd.isna(latest_pvt):
                latest_pvt = 0.0
            if pd.isna(latest_vwap_change):
                latest_vwap_change = 0.0
            
            # 非线性转换
            volume_ratio_sentiment = np.tanh((latest_volume_ratio - 1) * 2)
            volume_rs_sentiment = np.tanh((latest_volume_rs - 1) * 1.5)
            
            pvt_max = data['price_volume_trend'].abs().max()
            if pvt_max > 0:
                pvt_sentiment = np.tanh(latest_pvt / pvt_max * 3)
            else:
                pvt_sentiment = 0.0
            
            vwap_sentiment = np.tanh(latest_vwap_change * 20)
            
            # 综合成交量分数
            final_volume_score = (
                volume_ratio_sentiment * 0.35 +
                volume_rs_sentiment * 0.25 +
                pvt_sentiment * 0.25 +
                vwap_sentiment * 0.15
            ) * 100
            
            final_volume_score = TANH_MULTIPLIER * np.tanh(final_volume_score * TANH_SCALING_FACTOR)
            
            logger.debug(f"成交量计算 - 比率: {latest_volume_ratio:.2f}, 最终分数: {final_volume_score:.2f}")
            
            return final_volume_score
            
        except Exception as e:
            logger.error(f"计算成交量指标失败: {str(e)}")
            return 0.0
    
    def _calculate_put_call_score(self, data: pd.DataFrame) -> float:
        """计算看跌看涨比率分数 (简化版本)"""
        try:
            # 创建数据副本
            data = data.copy()
            
            # 基于价格趋势和成交量来估算看跌看涨比率
            data.loc[:, 'price_trend'] = data['Close'].rolling(window=10).mean()
            data.loc[:, 'trend_strength'] = (data['Close'] - data['price_trend']) / data['price_trend']
            
            latest_trend_strength = data['trend_strength'].iloc[-1]
            
            if pd.isna(latest_trend_strength):
                latest_trend_strength = 0.0
            
            put_call_score = TANH_MULTIPLIER * np.tanh(latest_trend_strength * 100 * TANH_SCALING_FACTOR)
            
            return put_call_score
            
        except Exception as e:
            logger.error(f"计算看跌看涨比率失败: {str(e)}")
            return 0.0
    
    def _get_sentiment_level(self, fear_greed_index: float) -> str:
        """根据恐贪指标确定市场情绪状态"""
        if fear_greed_index >= 80:
            return "极度贪婪"
        elif fear_greed_index >= 60:
            return "贪婪"
        elif fear_greed_index >= 40:
            return "轻微贪婪"
        elif fear_greed_index >= 20:
            return "中性偏贪婪"
        elif fear_greed_index >= -20:
            return "中性"
        elif fear_greed_index >= -40:
            return "中性偏恐惧"
        elif fear_greed_index >= -60:
            return "恐惧"
        elif fear_greed_index >= -80:
            return "极度恐惧"
        else:
            return "极度恐惧"
    
    def _get_investment_advice(self, fear_greed_index: float) -> str:
        """根据恐贪指数生成投资建议"""
        if fear_greed_index >= 80:
            return "极度卖出"
        elif fear_greed_index >= 60:
            return "卖出"
        elif fear_greed_index >= 40:
            return "谨慎持有"
        elif fear_greed_index >= 20:
            return "观望"
        elif fear_greed_index >= -20:
            return "中性"
        elif fear_greed_index >= -40:
            return "观望"
        elif fear_greed_index >= -60:
            return "买入"
        elif fear_greed_index >= -80:
            return "极度买入"
        else:
            return "极度买入"
    
    def calculate_historical_fear_greed(self, stock_data: pd.DataFrame, symbol: str = None) -> List[Dict]:
        """
        计算历史恐贪指标
        
        Args:
            stock_data: 包含OHLCV数据的DataFrame
            symbol: 股票代码（可选）
            
        Returns:
            历史恐贪指标列表
        """
        try:
            if stock_data.empty or len(stock_data) < 50:
                raise ValueError(f"数据不足，无法计算历史恐贪指标")
            
            history_results = []
            
            # 计算每日的恐贪指标
            for i in range(20, len(stock_data)):  # 需要至少20天数据来计算指标
                daily_data = stock_data.iloc[:i+1].copy()
                if len(daily_data) >= 20:
                    try:
                        daily_result = self.calculate_fear_greed_index_from_data(daily_data, symbol)
                        
                        # 获取当日的完整市场数据
                        latest_data = daily_data.iloc[-1]
                        
                        # 提取组件数据
                        components = daily_result['components']
                        
                        history_results.append({
                            'date': daily_data.index[-1].strftime('%Y-%m-%d'),
                            'fear_greed_index': float(round(daily_result['fear_greed_index'], ROUND_PRECISION)),
                            'sentiment': daily_result['sentiment'],
                            'investment_advice': daily_result['investment_advice'],
                            # 完整的OHLCV数据
                            'open_price': float(round(latest_data['Open'], ROUND_PRECISION)),
                            'high_price': float(round(latest_data['High'], ROUND_PRECISION)),
                            'low_price': float(round(latest_data['Low'], ROUND_PRECISION)),
                            'close_price': float(round(latest_data['Close'], ROUND_PRECISION)),
                            'volume': int(latest_data['Volume']),
                            'rsi': float(round(components.get('momentum', 0), 3)),
                            'volatility': float(round(components.get('volatility', 0), 4)),
                            'volume_ratio': float(round(components.get('volume', 0), 2)),
                            'put_call_ratio': float(round(components.get('put_call_ratio', 0), 3)),
                            'components': components  # 保留原始组件数据
                        })
                    except Exception as e:
                        logger.warning(f"计算第 {i} 天的恐贪指标失败: {e}")
                        continue
            
            logger.info(f"计算历史恐贪指标完成，共 {len(history_results)} 条记录")
            return history_results
            
        except Exception as e:
            logger.error(f"计算历史恐贪指标失败: {str(e)}")
            return []


# 创建全局实例
fear_greed_processor = FearGreedProcessor()
